﻿using System;
using System.Collections.Generic;
using System.Text;

namespace OpiConsole
{
    public class CommandParser
    {
        private String m_sCommand;
        private String m_sTemp;
        private String m_sCommandName;
        private bool m_bIsQuoted;		//Quoted (")
        private bool m_bIsEscaped;		//Escaped (\)
        private bool m_bNameDone;		//End of the command name
        private bool m_bShouldAddLast;	//??????
        List<String> m_lArgs;

        public CommandParser(String str)
        {
            m_sCommand = str;
        }

        public bool Parse(out String name, out List<String> args)
        {
            char spaceChar = ' ';

            //Clear the input
            m_lArgs = new List<string>();
            name = "";

            //Check to see if its just the command name
            if (m_sCommand.IndexOf(spaceChar) == -1)
            {
                name = m_sCommand;
                args = m_lArgs;
                return true;
            }

            //Rest Variables
            m_bIsEscaped = false;
            m_bIsQuoted = false;
            m_bNameDone = false;
            m_bShouldAddLast = true;

            for (int i = 0; i < m_sCommand.Length; i++)
            {
                if (!HandleChar(m_sCommand[i]))
                {
                    args = m_lArgs;
                    return false;
                }
            }

            if (m_bShouldAddLast)
                MoveTempString();

            args = m_lArgs;
            name = m_sCommandName;
            return true;
        }

        bool HandleChar(char chr)
        {
            char spaceChar = ' ';
            char escapeChar = '\\';
            char quoteChar = '\"';

            //If the char is a space
            if (chr == spaceChar)
            {
                //If we don't have a quote then just add it as a new arg
                if (!m_bIsQuoted)
                {
                    MoveTempString();
                }
                else
                {
                    m_sTemp += (spaceChar);
                    m_bShouldAddLast = true;
                }
            }
            
            //If we've found a quote char
            else if (chr == quoteChar)
            {
                //If we have just escaped then ignore this and add as a normal char
                if (m_bIsEscaped)
                {
                    m_sTemp += quoteChar;
                    m_bIsEscaped = false;
                    m_bShouldAddLast = true;
                }

                //If this is the end of a quote then the whole quoted section is one arg
                else if (m_bIsQuoted)
                {
                    MoveTempString();
                    m_bIsQuoted = false;
                }

                //Start a new quotation
                else
                    m_bIsQuoted = true;
            }
            
            //If we've found an escape char
            else if (chr == escapeChar)
            {
                //If we have already escaped then add it as normal text
                if (m_bIsEscaped)
                {
                    m_sTemp += escapeChar;
                    m_bIsEscaped = false;
                    m_bShouldAddLast = true;
                }
                //If this is the first escape then escape the next char
                else
                    m_bIsEscaped = true;
            }

            //Any other character
            else
            {
                //If we have escaped then the next char is not going to be used
                if (m_bIsEscaped)
                    return false;

                m_sTemp += chr;
                m_bShouldAddLast = true;
            }

            return true;
        }

        private void MoveTempString()
        {
            //If we already have the name
            if (m_bNameDone)
            {
                if (m_sTemp.Length != 0)
                    m_lArgs.Add(m_sTemp);
            }

            //If we don't have a command name then this is the first arg
            //and will become the command name.
            else
            {
                m_sCommandName = m_sTemp;
                m_bNameDone = true;
            }
            m_bShouldAddLast = false;
            m_sTemp = "";
        }
    }
}
